home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 112_01.zip / DISKDOC.C < prev    next >
Text File  |  1993-06-19  |  18KB  |  900 lines

  1. /*  TAB s5,4
  2.  *
  3.  *  diskdoc
  4.  *  a utility for diskette maintainance
  5.  *
  6.  *  by egil kvaleberg
  7.  *     studpost 111
  8.  *     n7034 trondheim-nth
  9.  *     norway
  10.  *
  11.  *  diskdoc is intended for use and distribution
  12.  *  among amateur computer users only. please respect this.
  13.  *
  14.  *  compile using the Ron Cain small C compiler as distributed
  15.  *  by The Code Works. if you use the version of the compiler shown
  16.  *  in the Dr. Dobbs May 1980 issue, you must ensure that the stack
  17.  *  is properly initialized. if you use another C compiler, you'll
  18.  *  have to revise (parts of) the file ddocsys.c. good luck.
  19.  */
  20.  
  21. #define VERSION "ver c - 1 nov 81"
  22. #define WHOMADEIT "by egil kvaleberg"
  23.  
  24. /*
  25.  *  constants, change as required
  26.  */
  27.  
  28. #define SECLEN 128    /* sector length, 128 or 256 is ok */
  29.  
  30. #define VOID 229    /* character used when zeroing diskette */
  31.  
  32. #define DHOME 26    /* default home character */
  33. #define ESC 27        /* lead-in character */
  34.  
  35. #define CR 13        /* various ascii equates */
  36. #define LF 10
  37. #define BS 8
  38. #define QOT 39
  39.  
  40. /*
  41.  *  globals
  42.  */
  43.  
  44. int tracks,        /* number of tracks and sectors */
  45.     sectors,firstsector;
  46.  
  47. int dfltrk,dflsec;    /* default values */
  48. char dfldrv;
  49.  
  50. char secbuf[SECLEN];    /* buffer for patch */
  51. char vfybuf[SECLEN];    /* buffer for write verify */
  52.  
  53. int home;        /* character to move cursor home */
  54.  
  55. /*
  56.  *  begin
  57.  */
  58.  
  59. main()
  60. {   char cmd;
  61.     dfldrv='a'; dfltrk=0; dflsec=0;
  62.     home=DHOME;
  63.     if (sysok()==0) exit();
  64.     nl(); puts("welcome to the world of diskdoc "); 
  65.     nl(); puts(VERSION); nl(); puts(WHOMADEIT);
  66.     nl(); nl(); puts("kindly enter your request");
  67.     cmd=0;
  68.     while (cmd!='e') {
  69.     nl();
  70.     puts("adapt, backup, compare, exit, patch, scan, test or zero ?");
  71.     cmd=getcmd("abcepstz",'e');
  72.     if (cmd=='a') adapt();
  73.     else if (cmd=='b') backup();
  74.     else if (cmd=='c') compare();
  75.     else if (cmd=='p') patch();
  76.     else if (cmd=='s') scan();
  77.     else if (cmd=='t') test();
  78.     else if (cmd=='z') zero();
  79.     else {
  80.         nl(); puts("re-insert the system diskette and type <cr> ");
  81.         if (conlower()!=CR) cmd=0;
  82.         nl();
  83.     }
  84.     }
  85.     nl(); puts("thanks for having consulted diskdoc");
  86.     exit();
  87. }
  88.  
  89. /*
  90.  *  adapt to terminal
  91.  */
  92.  
  93. adapt()
  94. {   nl(); nl(); puts("type character to move cursor home..");
  95.     putbyte(home=conin());
  96.     if (home==ESC) {
  97.     puts(".."); putbyte(home=conin());
  98.     home=home+128;
  99.     }
  100.     nl();
  101. }
  102.  
  103. /*
  104.  *  backup, copy entire diskette
  105.  */
  106.  
  107. backup()
  108. {   int trk,ftrk,ltrk;
  109.     char sdrive,ddrive; char *adr;
  110.     if (seltwo(&sdrive,&ddrive,
  111.            "source diskette in drive (a-h) ?",
  112.            "destination diskette in drive (a-h) ?")==0) return;
  113.     if (cont()==0) return;
  114.     trk=-1; 
  115.     while (nextt(&trk)) {
  116.     adr=botmem();
  117.     ftrk=trk;
  118.     if (baksel(sdrive,ddrive,"source")==0) return;
  119.     while (1) {
  120.         if (readtrk(trk,adr)==0) return;
  121.         ltrk=trk;
  122.         adr=adr+(SECLEN*sectors);
  123.         if ((adr+(SECLEN*sectors))>=topmem()) break;
  124.         if (nextt(&trk)==0) break;
  125.     }
  126.     adr=botmem(); trk=ftrk;
  127.     if (baksel(ddrive,sdrive,"destination")==0) return;
  128.     while (1) {
  129.         if (writetrk(trk,adr)==0) {
  130.         puts("backup aborted"); nl();
  131.         return;
  132.         }
  133.         if (trk>=ltrk) break;
  134.         nextt(&trk);
  135.         adr=adr+(SECLEN*sectors);
  136.     }
  137.     }
  138.     nl(); puts("backup finished"); nl();
  139. }
  140.  
  141. /*
  142.  *  compare contents of two diskettes
  143.  */
  144.  
  145. compare()
  146. {   int trk,sec;
  147.     char drive1,drive2;
  148.     char *adr1,*adr2;
  149.     if (seltwo(&drive1,&drive2,
  150.            "compare diskette in drive (a-h) ?",
  151.            "to diskette in drive (a-h) ?")==0) return;
  152.     if (cont()==0) return;
  153.     trk=-1; 
  154.     while (nextt(&trk)) {
  155.     adr1=botmem();
  156.     adr2=adr1+(SECLEN*sectors);
  157.     if (baksel(drive1,drive2,"first")==0) return;
  158.     if (readtrk(trk,adr1)==0) return;
  159.     if (baksel(drive2,drive1,"second")==0) return;
  160.     if (readtrk(trk,adr2)==0) return;
  161.     sec=firstsector-1;
  162.     while (nexts(&sec)) {
  163.         if (eqsec(adr1,adr2)==0) {
  164.         dfltrk=trk; dflsec=qskew(sec);
  165.         nl(); puts("compare error in "); putsec(dfltrk,dflsec);
  166.         }
  167.         adr1=adr1+SECLEN; adr2=adr2+SECLEN;
  168.     }
  169.     }
  170.     nl(); puts("compare finished"); nl();
  171. }
  172.  
  173. /*
  174.  *  zero diskette contents
  175.  */
  176.  
  177. zero()
  178. {   int trk,trkmax,n,m;
  179.     char *adr;
  180.     nl(); nl(); puts("zero diskette in drive (a-h) ?");
  181.     if (getsel()==0) return;
  182.     nl(); puts("zero how many tracks (0-"); putnum(tracks-1); puts(") ?");
  183.     trkmax=getnum(tracks-1,0,tracks-1);
  184.     if (cont()==0) return;
  185.     n=0; adr=botmem(); m=SECLEN*sectors;
  186.     while (n<m) adr[n++]=VOID;
  187.     trk=0;
  188.     while (trk<trkmax) {
  189.     if (writetrk(trk,botmem())==0) return;
  190.     nextt(&trk);
  191.     }
  192.     nl();
  193. }
  194.  
  195. /*
  196.  *  test a diskette by writing a pattern first,
  197.  *  and then reading it back
  198.  */
  199.  
  200. test()
  201. {   int trk,sec,n;
  202.     char *adr,*p;
  203.     nl(); nl(); puts("diskette to test in drive (a-h) ?");
  204.     if (getsel()==0) return;
  205.     nl(); puts("the test will destroy the contents of the diskette");
  206.     if (cont()==0) return;
  207.     p="**** diskdoc **** test pattern *"; /* length is 32 */
  208.     n=0;        /* fill pattern to compare to */
  209.     while (n<SECLEN) {
  210.     secbuf[n]=p[n&31];
  211.     ++n;
  212.     }
  213.     puts("does diskette already contain test pattern (y-n) ?");
  214.     if (getcmd("yn",'n')=='n') {
  215.     nl(); puts("writing test pattern...");
  216.     trk=0; sec=firstsector-1;
  217.     while (next(&trk,&sec)) {
  218.         secbuf[0]=trk; secbuf[1]=qskew(sec);
  219.         if (tstbrk()) return 0;
  220.         if (write(trk,qskew(sec),secbuf)==0) {
  221.         nl(); puts("error writing "); putsec(trk,qskew(sec)); nl();
  222.         return 0;
  223.         }
  224.     }
  225.     }
  226.     trk=-1;        /* read test */
  227.     nl(); puts("reading test pattern...");
  228.     while (nextt(&trk)) {
  229.     if (readtrk(trk,botmem())==0) return;
  230.     sec=firstsector-1; adr=botmem();
  231.     while (nexts(&sec)) {
  232.         secbuf[0]=trk; secbuf[1]=qskew(sec);
  233.         if (eqsec(adr,secbuf)==0) {
  234.         dfltrk=trk; dflsec=qskew(sec);
  235.         nl(); puts("bad test pattern in "); putsec(dfltrk,dflsec);
  236.         }
  237.         adr=adr+SECLEN;
  238.     }
  239.     }
  240.     nl(); puts("test completed"); nl();
  241. }
  242.  
  243. /*
  244.  *  scan: check all sectors for crc errors etc.
  245.  */
  246.  
  247. scan()
  248. {   int trk,sec;
  249.     nl(); nl(); puts("scan diskette in drive (a-h) ?");
  250.     if (getsel()==0) return;
  251.     if (cont()) {
  252.     nl();
  253.     trk=0; sec=firstsector-1;
  254.     while (next(&trk,&sec)) {
  255.         if (tstbrk()) return;
  256.         if (sec==firstsector) {
  257.         puts("track "); putnum(trk); conout(CR);
  258.         }
  259.         if (read(trk,qskew(sec),vfybuf)==0) {
  260.         puts("the dubious quality of ");
  261.         putsec(trk,qskew(sec)); puts(" has been detected"); nl();
  262.         dfltrk=trk; dflsec=qskew(sec);
  263.         }
  264.     }
  265.     puts("scan finished"); nl();
  266.     }
  267. }
  268.  
  269. /*
  270.  *  patch
  271.  */
  272.  
  273. patch()
  274. {   int pos;
  275.     nl(); nl(); puts("patch diskette in drive (a-h) ?");
  276.     if (getsel()==0) return;
  277.     if (dflsec<firstsector) dflsec=firstsector;
  278.     getsec(&dfltrk,&dflsec);
  279.     putnls(60); gohome();
  280.     puts("<sp>=next-byte, <bs>=previous-byte, <cr>=next-line, <hex>, ',"); 
  281.     nl();
  282.     puts("next, logical-next, trk-sec, re-read, shift-bit, write or quit ?");
  283.     putnls(4);
  284.     puts("    +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f");
  285.     puts("   0123456789abcdef");
  286.     pos=pread();
  287.     while ((pos=patchcmd(pos))>=0);
  288.     putnls(60);
  289. }
  290.  
  291. /*
  292.  *  one patch command
  293.  *  return new position, -1 if quit
  294.  */
  295.  
  296. patchcmd(pos)
  297.     int pos;
  298. {   char cmd,dig;
  299.     cmd=conlower();
  300.     if (cmd=='q') return -1;
  301.     else if (cmd==' ') pos=(pos+1)%SECLEN;
  302.     else if (cmd==BS)  pos=(pos+SECLEN-1)%SECLEN;
  303.     else if (cmd==CR)  pos=(pos+16)%SECLEN;
  304.     else if (cmd=='s') pos=pshift(pos);
  305.     else if (cmd=='n') pos=pnsect();
  306.     else if (cmd=='l') pos=plsect();
  307.     else if (cmd=='t') pos=psetts();
  308.     else if (cmd=='r') pos=pread();
  309.     else if (cmd=='w') pos=pwrite();
  310.     else if (cmd==QOT) {
  311.     conout(QOT);
  312.     cmd=conin();
  313.     conout(BS);
  314.     if (cmd==BS) tbyte(pos);
  315.     else pos=patchpos(pos,cmd);
  316.     }
  317.     else if ((dig=makhex(cmd))>=0) {
  318.     putdig(dig);
  319.     while ((cmd=conlower())!=BS) if (makhex(cmd)>=0) break;
  320.     conout(BS);
  321.     if (cmd==BS) tbyte(pos);
  322.     else pos=patchpos(pos,(dig<<4)+makhex(cmd));
  323.     }
  324.     return poscur(pos);
  325. }
  326.  
  327. /*
  328.  *  patch one byte, update screen
  329.  *  and return new position
  330.  */
  331.  
  332. patchpos(pos,new)
  333.     int pos,new;
  334. {   int tpos;
  335.     secbuf[pos]=new;
  336.     tpos=pos++; 
  337.     if (pos>=SECLEN) pos=0;
  338.     while (tbyte(tpos++)>0);
  339.     return pos;
  340. }
  341.  
  342. /*
  343.  *  shift one bit at current position
  344.  */
  345.  
  346. pshift(pos)
  347.     int pos;
  348. {   int tpos,byte,carry;
  349.     tpos=pos;
  350.     carry=0;
  351.     while (1) {
  352.     byte=secbuf[tpos]&255;
  353.     secbuf[tpos]=(byte>>1)|(carry<<7);
  354.     carry=byte&1;
  355.     if (tbyte(tpos++)==0) break;
  356.     }
  357.     return poscur(pos);
  358. }
  359.  
  360. /*
  361.  *  next sector
  362.  */
  363.  
  364. pnsect()
  365. {   next(&dfltrk,&dflsec);
  366.     return pread();
  367. }
  368.  
  369. /*
  370.  *  next logical sector
  371.  *  useful only in systems with mapping between logical and physical
  372.  *  sector numbers
  373.  */
  374.  
  375. plsect()
  376. {   lnext(&dfltrk,&dflsec);
  377.     return pread();
  378. }
  379.  
  380. /*
  381.  *  set new track/sector address
  382.  */
  383.  
  384. psetts()
  385. {   gohome(); putnls(2); putsps(38);
  386.     gohome(); nl();
  387.     getsec(&dfltrk,&dflsec);
  388.     return pread();
  389. }
  390.  
  391. /*
  392.  *  read and display one sector during patch
  393.  */
  394.  
  395.  pread()
  396.  {  char *p;
  397.     int pos;
  398.     pos=0;
  399.     while (pos<SECLEN) secbuf[pos++]=0;
  400.     gohome(); putnls(2);
  401.     if (read(dfltrk,dflsec,secbuf)==0) puts("error reading ");
  402.     putsec(dfltrk,dflsec);
  403.     putsps(18); nl(); putsps(24);
  404.     poscur(pos=0);
  405.     while (tbyte(pos++));
  406.     return poscur(0);
  407.  }
  408.  
  409. /*
  410.  *  write one sector during patch
  411.  *  verify by reading back
  412.  */
  413.  
  414. pwrite()
  415. {   int c;
  416.     gohome(); putnls(2); c=cont();
  417.     gohome(); putnls(3);
  418.     if (c) {
  419.     if (write(dfltrk,dflsec,secbuf)==0) puts("error writing");
  420.     else if (read(dfltrk,dflsec,vfybuf)==0) puts("error reading back");
  421.     else if (eqsec(secbuf,vfybuf)==0) puts("error on read verify");
  422.     }
  423.     putsps(22);
  424.     return poscur(0);
  425. }
  426.  
  427. /*
  428.  *  position cursor during patch
  429.  *  position is returned
  430.  */
  431.  
  432. poscur(pos)
  433.     int pos;
  434. {   int n;
  435.     gohome(); putnls(6); puts("00  ");
  436.     n=pos/16; while (n--) conout(LF);
  437.     n=(pos/16)*16; while (n<pos) tbyte(n++);
  438.     return pos;
  439. }
  440.  
  441. /*
  442.  *  print byte in patch table
  443.  *  returns -1 if end of line
  444.  *         0 if end of page
  445.  *         1 otherwise
  446.  */
  447.  
  448. tbyte(pos)
  449.     int pos;
  450. {   putbyte(secbuf[pos++]); /* print byte */
  451.     puts(" "); 
  452.     if (pos%16) return 1;
  453.     puts(" '");         /* print ascii equivalents */
  454.     pos=pos-16;
  455.     while (1) {
  456.     conout(makep(secbuf[pos++]));
  457.     if ((pos%16)==0) break;
  458.     }
  459.     puts("'"); nl();
  460.     if (pos==SECLEN) return 0;
  461.     putbyte(pos);        /* print address too */
  462.     puts("  ");
  463.     return -1;
  464. }
  465.  
  466. /*
  467.  *  read one track
  468.  *  assume that drive is already selected
  469.  */
  470.  
  471. readtrk(trk,adr)
  472.     int trk;
  473.     char *adr;
  474. {   int sec;
  475.     char cmd;
  476.     sec=firstsector;
  477.     while (1) {
  478.     if (tstbrk()) return 0;
  479.     if (read(trk,qskew(sec),adr)==0) {
  480.         nl(); puts("error reading "); putsec(trk,qskew(sec));
  481.         nl(); puts("continue, retry or quit ?");
  482.         cmd=getcmd("qcr",'r');
  483.         if (cmd=='r') {
  484.         read(0,firstsector,vfybuf); /* reposition */
  485.         continue;
  486.         }
  487.         dfltrk=trk; dflsec=qskew(sec);
  488.         if (cmd=='q') return 0;
  489.     }
  490.     adr=adr+SECLEN;
  491.     if (nexts(&sec)==0) return 1;
  492.     }
  493. }
  494.  
  495. /*
  496.  *  write one track
  497.  */
  498.  
  499. writetrk(trk,adr)
  500.     int trk;
  501.     char *adr;
  502. {   int sec;
  503.     sec=firstsector;
  504.     while (1) {
  505.     if (tstbrk()) return 0;
  506.     if (write(trk,qskew(sec),adr)==0) {
  507.         nl(); puts("error writing "); putsec(trk,qskew(sec)); nl();
  508.         return 0;
  509.     }
  510.     adr=adr+SECLEN;
  511.     if (nexts(&sec)==0) return 1;
  512.     }
  513. }
  514.  
  515. /*
  516.  *  get track and sector numbers
  517.  */
  518.  
  519. getsec(t,s)
  520.     int *t,*s;
  521. {   nl(); puts("track (0-"); putnum(tracks-1); puts(") ?");
  522.     *t=getnum(*t,0,tracks-1);
  523.     nl(); puts("sector ("); putnum(firstsector); puts("-"); 
  524.     putnum(sectors-1+firstsector); puts(") ?");
  525.     *s=getnum(*s,firstsector,sectors-1+firstsector);
  526. }
  527.  
  528. /*
  529.  *  get drive name, select and reset
  530.  *  0 if cannot select
  531.  */
  532.  
  533. getsel()
  534. {   if (seldrv((dfldrv=getdrv(dfldrv)),&tracks,§ors,&firstsector)==0) {
  535.     nl(); puts("drive not ready"); nl();
  536.     return 0;
  537.     }
  538.     rstdrv();
  539.     return 1;
  540. }
  541.  
  542. /*
  543.  *  select two drives
  544.  *  parameters of drives selected must be equivalent
  545.  */
  546.  
  547. seltwo(d1,d2,text1,text2)
  548.     char *d1,*d2;
  549.     char *text1,*text2;
  550. {   int dtracks,dsectors,dfirstsector;
  551.     nl(); nl(); puts(text1);
  552.     *d1=dfldrv=getdrv(dfldrv);
  553.     nl(); puts(text2);
  554.     *d2=getdrv(dfldrv);
  555.     seldrv(*d1,&tracks,§ors,&firstsector);       /* establish constants */
  556.     rstdrv();
  557.     seldrv(*d2,&dtracks,&dsectors,&dfirstsector);
  558.     rstdrv();
  559.     if ((dtracks!=tracks)|(dsectors!=sectors)|(dfirstsector!=firstsector)) {
  560.     nl(); puts("drives are not compatible"); nl();
  561.     return 0;
  562.     }
  563.     return 1;
  564. }
  565.  
  566. /*
  567.  *  increment track and sector, 0 if no more
  568.  */
  569.  
  570. next(t,s)
  571.     int *t,*s;
  572. {   if (nexts(s)) return 1;
  573.     return nextt(t);
  574. }
  575.  
  576. /*
  577.  *  increment sector, 0 if no more
  578.  */
  579.  
  580. nexts(s)
  581.     int *s;
  582. {   if ((++*s)>(sectors-1+firstsector)) {
  583.     *s=firstsector;
  584.     return 0;
  585.     }
  586.     return 1;
  587. }
  588.  
  589. /*
  590.  *  increment track, 0 if no more
  591.  */
  592.  
  593. nextt(t)
  594.     int *t;
  595. {   if ((++*t)>=tracks) return *t=0;
  596.     return 1;
  597. }
  598.  
  599. /*
  600.  *  return skewed sector number, skew factor is two.
  601.  *  this function is not equivalent to lnext().
  602.  *  the only purpose of this function is to optimize
  603.  *  scan and backup operations.
  604.  *  hard disks will operate faster without a skew.
  605.  *  hard disk is assumed if there are more than 100 tracks (!).
  606.  */
  607.  
  608. qskew(sec)
  609.     int sec;
  610. {   if (tracks>100) return sec;
  611.     sec=sec+sec-firstsector;
  612.     if (sec>(sectors-1+firstsector))
  613.     return sec-sectors+((sectors&1)==0);
  614.     return sec;
  615. }
  616.  
  617. /*
  618.  *  compare two sectors
  619.  *  false if not equal
  620.  */
  621.  
  622. eqsec(sec1,sec2)
  623.     char *sec1,*sec2;
  624. {   int n;
  625.     n=0;
  626.     while (n<SECLEN) {
  627.     if (sec1[n]!=sec2[n]) return 0;
  628.     ++n;
  629.     }
  630.     return 1;
  631. }
  632.  
  633. /*
  634.  *  select drive during backup
  635.  *  return 0 if something fishy
  636.  */
  637.  
  638. baksel(drv,otherdrv,name)
  639.     char drv,otherdrv; char *name;
  640. {   int dummy;
  641.     if (drv==otherdrv) {
  642.     nl(); puts("insert "); puts(name); puts(" diskette");
  643.     if (cont()==0) return 0;
  644.     }
  645.     if (seldrv(drv,&dummy,&dummy,&dummy)==0) {
  646.     nl(); puts(name); puts(" drive is not ready"); nl();
  647.     return 0;
  648.     }
  649.     return 1;
  650. }
  651.  
  652. /*
  653.  *  get drive name, enter with default drive
  654.  */
  655.  
  656. getdrv(dfl)
  657.     char dfl;
  658. {   return getcmd("abcdefgh",dfl);
  659. }
  660.  
  661. /*
  662.  *  ask for continue or quit, true if continue
  663.  */
  664.  
  665. cont()
  666. {   char cmd;
  667.     nl(); puts("continue or quit ?");
  668.     cmd=getcmd("cq",'c'); nl();
  669.     return cmd=='c';
  670. }
  671.  
  672. /*
  673.  *  get single character command,
  674.  *  enter with possible commands and default command
  675.  */
  676.  
  677. getcmd(cmds,dfl)
  678.     char *cmds,dfl;
  679. {   char cmd,ch;
  680.     while (1) {
  681.     if ((cmd=conlower())==CR) cmd=dfl;
  682.     if (member(cmd,cmds)==0) continue;
  683.     conout(cmd);
  684.     ch=0;
  685.     while (ch!=BS) if ((ch=conlower())==CR) return cmd;
  686.     unput();
  687.     }
  688. }
  689.  
  690. /*
  691.  *  see if character can be found in a string
  692.  */
  693.  
  694. member(ch,str)
  695.     char ch,*str;
  696. {   while (*str) if (*str++==ch) return 1;
  697.     return 0;
  698. }
  699.  
  700. /*
  701.  *  get a unsigned decimal number
  702.  *  enter with default, minimun and maximum values
  703.  */
  704.  
  705. getnum(dfl,min,max)
  706.     int dfl,min,max;
  707. {   int n,digits,newn; char ch;
  708.     if (dfl>max) dfl=max;
  709.     n=digits=0;
  710.     while (1) {
  711.     if ((ch=conlower())==CR) {
  712.         if (digits) {
  713.         if (n>=min) break;
  714.         } else digits=putnum(n=dfl);
  715.     } else if (ch==BS) {
  716.         if (digits) {
  717.         --digits; n=n/10;
  718.         unput();
  719.         }
  720.     } else if (((makdec(ch))>=0) & ((newn=n*10+makdec(ch))<=max)) {
  721.         if ((newn>0)|(digits==0)) {
  722.         ++digits; n=newn;
  723.         conout(ch);
  724.         }
  725.     }
  726.     }
  727.     return n;
  728. }
  729.  
  730. /*
  731.  *  make character printing
  732.  */
  733.  
  734. makep(ch)
  735.     char ch;
  736. {   ch=ch&127;
  737.     if ((ch>=' ')&(ch<127)) return ch;
  738.     return ' ';
  739. }
  740.  
  741. /*
  742.  *  put track and sector numbers
  743.  */
  744.  
  745. putsec(t,s)
  746.     int t,s;
  747. {   puts("track "); putnum(t);
  748.     puts(" sector "); putnum(s);
  749. }
  750.  
  751. /*
  752.  *  put decimal number,
  753.  *  return number of digits printed
  754.  */
  755.  
  756. putnum(n)
  757.     int n;
  758. {   int d;
  759.     if (n>9) d=putnum(n/10);
  760.     else d=0;
  761.     putdig(n%10);
  762.     return d+1;
  763. }
  764.  
  765. /*
  766.  *  put hex byte
  767.  */
  768.  
  769. putbyte(n)
  770.     int n;
  771. {   putdig(n>>4);
  772.     putdig(n);
  773. }
  774.  
  775. /*
  776.  *  put digit
  777.  */
  778.  
  779. putdig(d)
  780.     int d;
  781. {   if ((d=d&15)>9) conout(d-10+'a');
  782.     else conout(d+'0');
  783. }
  784.  
  785. /*
  786.  *  put string
  787.  */
  788.  
  789. puts(str)
  790.     char *str;
  791. {   while (*str) conout(*str++);
  792. }
  793.  
  794. /*
  795.  *  put spaces
  796.  */
  797.  
  798. putsps(n)
  799.     int n;
  800. {   while (n--) conout(' ');
  801. }
  802.  
  803. /*
  804.  *  new line
  805.  */
  806.  
  807. nl()
  808. {   conout(CR); conout(LF);
  809. }
  810.  
  811. /*
  812.  *  put newlines
  813.  */
  814.  
  815. putnls(n)
  816.     int n;
  817. {   while (n--) nl();
  818. }
  819.  
  820. /*
  821.  *  erase previous character
  822.  */
  823.  
  824. unput()
  825. {   conout(BS); conout(' '); conout(BS);
  826. }
  827.  
  828. /*
  829.  *  move cursor to home position
  830.  */
  831.  
  832. gohome()
  833. {   if (home>=128) conout(ESC);
  834.     conout(home & 127);
  835. }
  836.  
  837. /*
  838.  *  convert decimal digit, -1 if error
  839.  */
  840.  
  841. makdec(ch)
  842.     char ch;
  843. {   if ((ch=ch-'0')<0) return -1;
  844.     if (ch<=9) return ch;
  845.     return -1;
  846. }
  847.  
  848. /*
  849.  *  convert hex digit, -1 if error
  850.  */
  851.  
  852. makhex(ch)
  853.     char ch;
  854. {   if (ch<'0') return -1;
  855.     if (ch<='9') return ch-'0';
  856.     if ((ch=ch-'a'+10)<10) return -1;
  857.     if (ch<=15) return ch;
  858.     return -1;
  859. }
  860.  
  861. /*
  862.  *  test for break, true if yes
  863.  */
  864.  
  865. tstbrk()
  866. {   if (const()) {
  867.     nl(); puts("break");
  868.     if (cont()==0) return 1;
  869.     }
  870.     return 0;
  871. }
  872.  
  873. /*
  874.  *  get character, no echo
  875.  *  upper case is converted to lower
  876.  */
  877.  
  878. conlower()
  879. {   char ch;
  880.     ch=conin();
  881.     if ((ch>='A')&(ch<='Z')) ch=ch+'a'-'A';
  882.     return ch;
  883. }
  884.  
  885. /*
  886.  *  system dependant functions
  887.  */
  888.  
  889. #include ddocsys.c
  890.  
  891. /*
  892.  *  the small C runtime library (as shown in the Dr. Dobbs
  893.  *  September 1980 issue and as distributed by The Code
  894.  *  Works) must be named ddoclib and edited
  895.  *  so that it contains the primitive functions only:
  896.  *  ccgchar,ccgint,ccpchar,...,ccmult,ccdiv
  897.  */
  898.  
  899. #include ddoclib
  900.